<template>
  <div class="verifyCode">
    <el-form status-icon :rules="rules" ref="form" :model="verifyForm">
      <el-form-item prop="value">
        <el-input prefix-icon="el-icon-key" v-model="verifyForm.value" placeholder="请输入验证码"/>
      </el-form-item>
    </el-form>
    <canvas
        :width="canvasWidth"
        :height="canvasHeight"
        ref="canvas"
        @click="renderCode"
        class="fx-verify-code"/>
  </div>
</template>

<script>
import VerifyCode from "@/tools/VerifyCode";

export default {
  name: "VerifyCode",
  props: {
    canvasWidth: {
      default() {
        return 130
      }
    },
    canvasHeight: {
      default() {
        return 38
      }
    },
  },
  mounted() {
    this.initCanvas()
    this.createVerifyCode()
    this.renderCode()
  },
  data() {
    const validateVerifyCode = (rule, value, cb) => {
      if (!value) {
        this.isVerified = false
        return cb(new Error('请输入验证码！'))
      }
      if (value.toLowerCase() !== this.verifyCode.toLowerCase()) {
        this.isVerified = false
        return cb(new Error('验证码不匹配！'))
      }
      this.isVerified = true
      cb()
    }
    return {
      isVerified: false,
      verifyCode: '',
      verifyForm: {
        value: ''
      },
      ctx: null,
      rules: {
        value: {required: true, validator: validateVerifyCode, trigger: 'blur'}
      }
    }
  },
  methods: {
    // 初始化画布
    initCanvas() {
      /** @type {HTMLCanvasElement} */
      this.ctx = this.$refs.canvas.getContext('2d')
      this.ctx.fillStyle = 'red'
    },
    // 验证码生成函数
    createVerifyCode(len = 5) {
      // 调用 tools 里封装的验证码生成工具来生成验证码
      this.verifyCode = VerifyCode.createVerifiedCode(len)
    },
    // 绘制验证码
    renderCode() {
      /** @type {HTMLCanvasElement} */

      this.createVerifyCode()

      // 先清除画布
      this.ctx.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)

      // 显示验证码
      for (let i = 0; i < this.verifyCode.length; i++) {
        // 随机颜色
        let fontColor = `#${Math.random().toString(16).slice(2, 8)}`
        // 设置透明度(0.8~1.0)
        fontColor += (Math.random() * 0.2 + 0.8).toString(16).slice(2, 4);
        // 实心字体
        this.ctx.fillStyle = fontColor
        // 随机字体(14~20px)
        const font = Math.floor(Math.random() * 7 + 16)
        this.ctx.font = `${font}px serif`
        // 计算每个字符所占的宽度
        const textWidth = this.ctx.measureText(this.verifyCode[i]).width
        // 文字对齐方式
        this.ctx.textAlign = 'start'
        this.ctx.textBaseline = 'top'
        // 绘制文字
        let dx = this.$refs.canvas.width * 0.15
        let dy = (this.$refs.canvas.height - font) / 2
        this.ctx.fillText(this.verifyCode[i], dx + i * 20, dy, textWidth)
      }

      // 绘制干扰项 干扰点
      for (let i = 0; i < 30; i++) {
        this.ctx.beginPath()
        let color = `#${Math.random().toString(16).slice(2, 8)}`
        color += (Math.random() * 0.2 + 0.8).toString(16).slice(2, 4);
        this.ctx.fillStyle = color
        let positionX = Math.random() * this.$refs.canvas.width
        let positionY = Math.random() * this.$refs.canvas.height
        this.ctx.arc(positionX, positionY, 1, 0, Math.PI * 2)
        this.ctx.fill()
      }
    }
  }
}
</script>

<style scoped>
.verifyCode {
  display: flex;
  align-items: center;
  width: 100%;
  margin-bottom: 22px;
}

.el-form-item {
  margin-bottom: 0;
}

.fx-verify-code {
  width: 130px;
  height: 38px;
  margin-left: 10px;
  cursor: pointer;
  background-image: linear-gradient(to right bottom, rgba(187, 225, 206, 0.4), #e8e5e5);
  border-radius: 4px;
  border: 1px solid rgb(187, 225, 206);
}

.fx-verify-code:hover {
  border-color: lightgreen;
  transition: .6s;
}
</style>
